<?php
/**
 * CSVクリエイター抽象クラス
 *
 * @author tanaka
 * @package File_Csv_Creator
 * @subpackage Abstract
 */
abstract class File_Csv_CreatorAbstract
{
	/**
	 * データの区切り文字
	 * @var string
	 */
	const VALUE_SEPARATOER = ',';
	
	/**
	 * CSV1行のデータ
	 * @var string
	 */
	const LINE_END = "\r\n";

	/**
	 * デフォルト日付フォーマット
	 * @var string
	 */
	protected static $_defaultDatetimeFormat = "Y/m/d";

	/**
	 * 日付フォーマット
	 * @var string
	 */
	protected $_datetimeFormat;

	/**
	 * 日付フォーマットをセットします。
	 * @param string $format
	 * @return void
	 */
	public function setDatetimeFormat($format)
	{
		$this->_datetimeFormat = $format;
	}

	/**
	 * デフォルト日付フォーマットをセットします。
	 * @param string $format
	 * @return void
	 */
	public static function setDefaultDatetimeFormat($format)
	{
		self::$_defaultDatetimeFormat = $format;
	}

	/**
	 * 日付フォーマットを取得します。
	 *
	 * @return string
	 */
	public function getDatetimeFormat()
	{
		return is_null($this->_datetimeFormat) ? self::$_defaultDatetimeFormat : $this->_datetimeFormat;
	}

	/**
	 * ヘッダ行を取得します。
	 *
	 * @return array
	 */
	abstract protected function _getHeaders();

	/**
	 * キー名を取得します。
	 *
	 * @return
	 */
	abstract protected function _getColumnKeys();

	/**
	 * 配列の値を調節します。
	 *
	 * @return array
	 */
	protected function _getFixdRowData($row)
	{
		// 特定の共通列だけ日時表記に変更
		$keys = array(
			'INSERT_DATE',
			'UPDATE_DATE',
			'DELETE_DATE'
		);
		
		foreach($keys as $key){
			if(isset($row[$key]) && $row[$key] instanceof DateTime){
				$row[$key] = $row[$key]->format('Y/m/d H:i');
			}
		}
		
		return $row;
	}

	/**
	 * 出力元データ
	 * @var array
	 */
	protected $_data = array();
	
	/**
	 * 
	 * @var unknown_type
	 */
	protected $_result;

	/**
	 * コンストラクタ
	 *
	 * @throws AppException
	 * @param array $rowset
	 * @return void
	 */
	public function __construct($data)
	{
		if (!count($this->_getColumnKeys())) {
			throw new AppException('列キーがない。');
		}

		if (count($this->_getHeaders()) && count($this->_getHeaders()) != count($this->_getColumnKeys())) {
			throw new AppException('ヘッダ行と列キーの数が合わない');
		}

		$this->_data = $data;
	}

	/**
	 * CSV形式の文字列を取得します。
	 *
	 * @return string
	 */
	public function __toString()
	{
		return $this->getCsv();
	}

	/**
	 * CSV形式の文字列を取得します。
	 *
	 * @return string
	 */
	public function getCsv()
	{
		if(is_null($this->_result)){
			$result = '';
			$line = '';
			$lineData = array();
	
			$headers = $this->_getHeaders();
			$colmnKeys = $this->_getColumnKeys();
	
			// ヘッダ行の生成
			if (count($headers)) {
	
				foreach ($headers as $header) {
					$lineData[] = $this->_createColumn($header);
				}
	
				$result .= implode(self::VALUE_SEPARATOER, $lineData) . self::LINE_END;
				$lineData = array();
			}
	
			foreach ($this->_data as $row) {
	
				$row = $this->_getFixdRowData($row);
	
				foreach ($colmnKeys as $key) {
	
					if (is_array($key)) {
						$holder = '';
						foreach ($key as $akey) {
							if (isset($row[$akey])) {
	
								if (strlen($holder)) {
									$holder .= ' ';
								}
	
								$holder .= $row[$akey];
							}
						}
	
						$lineData[] = $this->_createColumn($holder);
					}
					else if (isset($row[$key])) {
						$lineData[] = $this->_createColumn($row[$key]);
					}
					else {
						$lineData[] = $this->_createColumn(null);
					}
	
					$line = implode(self::VALUE_SEPARATOER, $lineData);
				}
	
				$result .= $line . self::LINE_END;
				$lineData = array();
			}
			
			$this->_result = $result;
			unset($this->_data);
		}

		return $this->_result;
	}

	/**
	 * データを整形します。
	 *
	 * @param mixed $columnData
	 * @return string
	 */
	protected function _createColumn($columnData)
	{
		if (is_null($columnData)) {
			return '""';
		}

		if ($columnData instanceof DateTime) {
			$columnData = $columnData->format($this->getDatetimeFormat());
		}

		// 改行の除去（半角スペースに置換）
		$columnData = str_replace(array("\r\n", "\n", "\r"), ' ', $columnData);

		// ダブルクォーテーションをシングルクォーテーションに変換
		return '"' . str_replace('"', '\'', $columnData) . '"';
	}
	
	/**
	 * データの件数を取得します。
	 * 
	 * @return int
	 */
	public function getRowCount()
	{
		return count($this->_data);
	}

	/**
	 * @var string
	 */
	protected $_filenamePrefix = '';
	
	/**
	 * ファイル拡張子
	 * @var string
	 */
	const FILENAME_SUFFIX = '.csv';
	
	/**
	 * ファイル名を取得
	 * 
	 * @return string
	 */
	public function getFileName()
	{
		return $this->_filenamePrefix . date('ymd') . self::FILENAME_SUFFIX;
	}
}
